use client::ServerSession;
use protocol::NativePacketPayload;
use response::{sha2_password, EOFPacket, ERRPacket, OKPacket, SwitchRequestPacket};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::select;
use tokio::sync::mpsc;
use tokio_stream::wrappers::UnboundedReceiverStream;
use std::error::Error;
use tokio::net::TcpStream;
use futures::{SinkExt as _, StreamExt as _};

mod client;
mod nativs;
mod protocol;
mod response;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {

    let mut sock = match TcpStream::connect("127.0.0.1:13306").await {
        Ok(s) => s,
        Err(e) => panic!("{e}")
    };
    
    let (tx, _rx) = mpsc::unbounded_channel::<Vec<u8>>();
    let mut rx = UnboundedReceiverStream::new(_rx);
    
    
    

    // 16k
    let mut buf = [0; 16384];
    let mut sess = ServerSession::new();
    loop {
        select! {
            Ok(n) = sock.read(&mut buf) => {
                println!("recv<-data: {:?}", &buf[..n]);
                let raw = &buf[..n];
                if !sess.has_native_capabilities() {
                    sess.initial_handshake_packet(raw);
                    let data = sess.connect("root", "baikai#1234", "");
                    tx.send(data).unwrap();
                } else {
                    

                    let mut payload = NativePacketPayload::new(raw);
                    match payload.read_header() {
                        0x00 => {
                            // 0x00 or 0xFE the OK packet header
                            let mut packet = OKPacket::new(payload);
                            packet.handle(sess.get_capability_flags());
                            println!("{:?}", packet);

                        }
                        0xFE => {
                            // EOF Packet
                            if !sess.is_connected() {
                                let mut packet = SwitchRequestPacket::new(payload);
                                packet.handle();
                                println!("{:?}", packet);

                                if packet.plugin_name == "caching_sha2_password" {
                                    println!("switch: {}", packet.plugin_name);
                                    // sha2_password("baikai#1234", salt)
                                } else if packet.plugin_name == "sha256_password" {
                                    println!("switch: {}", packet.plugin_name);
                                }
                            } else {
                                let mut packet = EOFPacket::new(payload);
                                packet.handle(sess.get_capability_flags());
                                println!("{:?}", packet);
                            }
                        },

                        0xFF => {
                            // ERR Packet
                            let mut packet = ERRPacket::new(payload);
                            packet.handle(sess.get_capability_flags());

                            println!("{:?}", packet);
                        },
                        _ => {}
                    }
                }

                
            },

            Some(data) = rx.next() => {
                println!("send->data: {:?}", data);
                // 发送数据
                let n = sock.write(&data).await;
                n.unwrap();

            },
            else => {
                // 未匹配
            }
        }
        
    }

    // loop {
    //     let ready = stream
    //         .ready(Interest::READABLE | Interest::WRITABLE)
    //         .await?;

    //     if ready.is_readable() {
    //         
    //         // Try to read data, this may still fail with `WouldBlock`
    //         // if the readiness event is a false positive.
    //         match stream.try_read(&mut data) {
    //             Ok(n) => {
    //                 let raw = &data[0..n];
    //                 println!("raw:{:?}", raw);
    //                 if !sess.is_inited_handshake_packet() {
    //                     sess.init_handshake_packet_with_raw(raw);
    //                 } else {
    //                     let mut npp = NativePacketPayload::new(raw);
    //                     match npp.header() {
    //                         // 0x00 => {
    //                         //     // 0x00 or 0xFE the OK packet header
    //                         // }
    //                         0xFE => {
    //                             // EOF Packet
    //                         }
    //                         0xFF => {
    //                             // ERR Packet
    //                             let mut epp = ERRPacketPayload::default();
    //                             epp.handler(raw);

    //                             println!("EPP: {:?}", epp);
    //                         }

    //                         _ => {}
    //                     }
    //                 }

    //             }
    //             Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
    //                 continue;
    //             }
    //             Err(e) => {
    //                 return Err(e.into());
    //             }
    //         }
    //     }

    //     if ready.is_writable() {
    //         // Try to write data, this may still fail with `WouldBlock`
    //         // if the readiness event is a false positive.
    //         // match stream.try_write(b"hello world") {
    //         //     Ok(n) => {
    //         //         println!("write {} bytes", n);
    //         //     }
    //         //     Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => continue,
    //         //     Err(e) => {
    //         //         return Err(e.into());
    //         //     }
    //         // }
    //     }
    // }

}

#[cfg(test)]
mod tests {
    use std::fs;

    use sha1::Sha1;

    #[test]
    fn test0() {
        // 全部payload的字节数：74 0 0

        // 10 => Protocal
        let data = &[74, 0, 0, 0, 10];
        let mut pos = 0;
        let payload_width = data[pos];
        assert_eq!(payload_width, 74);
        pos += 1;
        assert_eq!(data[pos], 0);
        pos += 1;
        assert_eq!(data[pos], 0);

        // Packet number: 0
        pos += 1;
        let packet_number = data[pos];
        assert_eq!(packet_number, 0);

        // Protocal
        pos += 1;
        let protocal = data[pos];
        assert_eq!(protocal, 10);
    }

    // 遇到\0就
    fn split_off(data: &[u8]) -> (&[u8], &[u8]) {
        let mut index = 0;
        for (i, n) in data.iter().enumerate() {
            index = i.to_owned();
            if *n == 0 {
                break;
            }
        }
        data.split_at(index)
    }

    #[test]
    fn test_version() {
        // 版本号，\x0为结束符
        let pos = 0;

        let data = &[56, 46, 48, 46, 51, 57, 0];

        let (left, right) = split_off(data);

        // let (left, right) = data.split_at(data.index(0x00));

        let version = String::from_utf8_lossy(&left);

        println!("s=={}", version);
    }

    #[test]
    fn test_thread_id() {
        // thread_id: 9
        let data = &[9, 0, 0, 0];

        let s = String::from_utf8_lossy(data);

        println!("s=={}", s);
    }

    #[test]
    fn test_salt_1() {
        // 48 => 0
        // salt

        let data = &[48, 58, 46, 15, 100, 95, 105, 118, 0];

        let s = String::from_utf8_lossy(data);

        println!("s=={}", s);
    }

    #[test]
    fn test_other() {
        // 服务器功能
        // 0xFFFF -> Server Capabilities
        // 0xFF => Server Language:  255    ID => 查看 information_schema.COLLATIONS.ID;
        // 0x02 => Server status  ===> SERVER_STATUS_AUTOCOMMIT = 2,   /**< Server in auto_commit mode */     include/mysql_com.h

        let data = &[255, 255, 255, 2, 0];

        let s = String::from_utf8_lossy(data);

        println!("s=={}", s);
    }

    #[test]
    fn test_msg() {
        // [74, 0, 0, 0, 10, 56, 46, 48, 46, 51, 57, 0, 9, 0, 0, 0, 48, 58, 46, 15, 100, 95, 105, 118, 0, 255, 255, 255, 2, 0, 255, 223, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 112, 58, 94, 35, 1, 38, 38, 21, 29, 59, 0, 99, 97, 99, 104, 105, 110, 103, 95, 115, 104, 97, 50, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0, 33, 0, 0, 1, 255, 132, 4, 35, 48, 56, 83, 48, 49, 71, 111, 116, 32, 112, 97, 99, 107, 101, 116, 115, 32, 111, 117, 116, 32, 111, 102, 32, 111, 114, 100, 101, 114]

        // 0xFF DF ==> Extended Server Capabiliities: 0xdfff
        // 21 => Authentication plugin length: 21
        // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 => Unused
        let data = &[255, 223, 21, 0];

        let s = String::from_utf8_lossy(data);

        println!("s=={}", s);
    }

    #[test]
    fn test_salt_2() {
        // 盐
        // [74, 0, 0, 0, 10, 56, 46, 48, 46, 51, 57, 0, 9, 0, 0, 0, 48, 58, 46, 15, 100, 95, 105, 118, 0, 255, 255, 255, 2, 0, 255, 223, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        //  1, 3, 112, 58, 94, 35,
        //  1, 38, 38, 21, 29, 59, 0, 99, 97, 99, 104, 105, 110, 103, 95, 115, 104, 97, 50, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0, 33, 0, 0, 1, 255, 132, 4, 35, 48, 56, 83, 48, 49, 71, 111, 116, 32, 112, 97, 99, 107, 101, 116, 115, 32, 111, 117, 116, 32, 111, 102, 32, 111, 114, 100, 101, 114]

        let data = &[1, 38, 38, 21, 29, 59, 0];

        let s = String::from_utf8_lossy(data);

        println!("s=={}", s);
    }

    #[test]
    fn test_auth_plugin() {
        // 加密方式
        // [74, 0, 0, 0, 10, 56, 46, 48, 46, 51, 57, 0, 9, 0, 0, 0, 48, 58, 46, 15, 100, 95, 105, 118, 0, 255, 255, 255, 2, 0, 255, 223, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        //  1, 3, 112, 58, 94, 35,
        //  1, 38, 38, 21, 29, 59, 0,
        //  99, 97, 99, 104, 105, 110, 103, 95, 115, 104, 97, 50, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0,
        //  33, 0, 0, 1,
        // 255, 132, 4, 35, 48, 56, 83, 48, 49, 71, 111, 116, 32, 112, 97, 99, 107, 101, 116, 115, 32, 111, 117, 116, 32, 111, 102, 32, 111, 114, 100, 101, 114]

        let data = &[
            99, 97, 99, 104, 105, 110, 103, 95, 115, 104, 97, 50, 95, 112, 97, 115, 115, 119, 111,
            114, 100, 0,
        ];

        let s = String::from_utf8_lossy(data);

        println!("s=={}", s);
    }

    #[test]
    fn test_() {
        // salt?
        // [74, 0, 0, 0, 10, 56, 46, 48, 46, 51, 57, 0, 9, 0, 0, 0, 48, 58, 46, 15, 100, 95, 105, 118, 0, 255, 255, 255, 2, 0, 255, 223, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        //  1, 3, 112, 58, 94, 35,
        //  1, 38, 38, 21, 29, 59, 0,
        //  99, 97, 99, 104, 105, 110, 103, 95, 115, 104, 97, 50, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0,
        //  33, 0, 0, 1,
        //  255, 132, 4, 35, 48, 56, 83, 48, 49, 71, 111, 116, 32, 112, 97, 99, 107, 101, 116, 115, 32, 111, 117, 116, 32, 111, 102, 32, 111, 114, 100, 101, 114]

        // 其他没用的信息
        // s==��#08
        // S01Got packets out of order
        // let data = &[
        //     255, 132, 4, 35, 48, 56, 83, 48, 49, 71, 111, 116, 32, 112, 97, 99, 107, 101, 116, 115,
        //     32, 111, 117, 116, 32, 111, 102, 32, 111, 114, 100, 101, 114,
        // ];
        let aa = 0x21;
        println!("{}", aa);
        // packet_length:  50, 0, 0
        let data = &[
            50, 0, 0, 1, // 0xFF
            255, //
            135, 4, 35, 48, 56, 83, 48, 49, 71, 111, 116, 32, 116, 105, 109, 101, 111, 117, 116, 32,
            114, 101, 97, 100, 105, 110, 103, 32, 99, 111, 109, 109, 117, 110, 105, 99, 97, 116,
            105, 111, 110, 32, 112, 97, 99, 107, 101, 116, 115,
        ];

        let s = String::from_utf8_lossy(data);

        println!("s=={}", s);
    }

    #[test]
    fn test() {

        let a: usize = 16777216;

        let b = a & 0xff;
        let c = (a >> 8) as u8;
        let d = (a >> 16) as u8;

        let e = (a >> 24) as u8;
        let f = (a >> 32) as u8;
        println!("{:?}", a);
        println!("{:?}", b);
        println!("{:?}", c);
        println!("{:?}", d);
        println!("{:?}", e);
        println!("{:?}", f);
        
    }

    #[test]
    fn test2() {

        let aa = [0, 0, 0, 1];

        let mut c: usize = 0 ;
        c |= (aa[0] & 0xff) as usize;
        c |= (aa[1] << 8) as usize;
        c |= (aa[2] << 16) as usize;
        c |= (aa[3] << 24) as usize;
        
        println!("c: {}", c);

        let a = [55, 2, 8, 1, 0, 64, 0, 0, 255, 23, 0, 0, 8, 51, 77, 125, 2, 100, 84, 29, 0, 255, 255, 255, 2, 0, 255, 223, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114, 24, 115, 111, 13, 122, 104, 53, 66, 37, 30, 108, 0, 99, 97, 99, 104, 105, 110, 103, 95, 115, 104, 97, 50, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 114, 111, 111, 116];
        
    }

    #[test]
    fn test3() {

        let data = [38, 0, 0, 2, 254, 115, 104, 97, 50, 53, 54, 95, 112, 97, 115, 115, 119, 111, 114, 100, 0, 80, 62, 24, 30, 47, 39, 83, 37, 43, 12, 75, 58, 37, 124, 53, 16, 50, 107, 90, 118, 0];

        let s = String::from_utf8_lossy(&data);
        println!("data: {}", s);

    }

    #[test]
    fn test4() {

        let data = [50, 0, 0, 3, 255, 135, 4, 35, 48, 56, 83, 48, 49, 71, 111, 116, 32, 116, 105, 109, 101, 111, 117, 116, 32, 114, 101, 97, 100, 105, 110, 103, 32, 99, 111, 109, 109, 117, 110, 105, 99, 97, 116, 105, 111, 110, 32, 112, 97, 99, 107, 101, 116, 115];

        let s = String::from_utf8_lossy(&data);
        println!("data: {}", s);

    }

    // https://cloud.tencent.com/developer/article/2247829
    #[test]
    fn sha2_password() {

        use sha256::digest;

        let db_user_password = "hello";
        let salt = "abc";

        /* SHA2(src) => digest_stage1 */
        let digest_stage1 = digest(db_user_password);
        /* SHA2(digest_stage1) => digest_stage2 */
        let digest_stage2 = digest(&digest_stage1);
        /* SHA2(digest_stage2, salt) => scramble_stage1 */
        let scramble_stage1 = digest(format!("{digest_stage2}{salt}"));
        /* XOR(digest_stage1, scramble_stage1) => scramble */
        println!("{scramble_stage1}");
        let digest_stage1 = digest_stage1.as_bytes();
        let scramble_stage1 = scramble_stage1.as_bytes();

        let mut scramble = Vec::new();
        for (i, n) in scramble_stage1.iter().enumerate(){
            scramble.push(digest_stage1[i] ^ n);
        }

        println!("{:?}", scramble);

    }

    #[test]
    fn native_password() {

        use sha1::{Sha1, Digest};

        let db_user_password = "hello";
        let salt = "abc";

         /* Two stage SHA1 hash of the password. */
        let mut hasher1 = Sha1::new();
        hasher1.update(db_user_password.as_bytes());
        let hash_stage1 = hasher1.finalize();
        println!("hash_stage1=>{:?}", hash_stage1);

        let mut hasher2 = Sha1::new();
        hasher2.update(hash_stage1);
        let hash_stage2 = hasher2.finalize();

        println!("hash_stage2=>{:?}", hash_stage2);

        /* create crypt string as sha1(message, hash_stage2) */
        let mut hasher = Sha1::new();
        hasher.update(salt.as_bytes());
        hasher.update(hash_stage2);
        let crypt = hasher.finalize();

        let mut scramble = Vec::new();
        for (i, n) in crypt.iter().enumerate(){
            scramble.push(hash_stage1[i] ^ n);
        }

        println!("{:?}", scramble);
        

    }
}
